home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / tenexdos.c < prev    next >
C/C++ Source or Header  |  1996-03-14  |  48KB  |  1,583 lines

  1. /*
  2.  * Program:    Tenexdos mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    12 June 1994
  13.  * Last Edited:    14 March 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39. #include <errno.h>
  40. #include <fcntl.h>
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <time.h>
  44. #include <sys/stat.h>
  45. #include <dos.h>
  46. #include <io.h>
  47. #include "tenexdos.h"
  48. #include "rfc822.h"
  49. #include "misc.h"
  50. #include "dummy.h"
  51.  
  52. /* Tenexdos mail routines */
  53.  
  54.  
  55. /* Driver dispatch used by MAIL */
  56.  
  57. DRIVER tenexdosdriver = {
  58.   "tenexdos",            /* driver name */
  59.   (DRIVER *) NIL,        /* next driver */
  60.   tenexdos_valid,        /* mailbox is valid for us */
  61.   tenexdos_parameters,        /* manipulate parameters */
  62.   tenexdos_find,        /* find mailboxes */
  63.   tenexdos_find_bboards,    /* find bboards */
  64.   tenexdos_find_all,        /* find all mailboxes */
  65.   tenexdos_find_bboards,    /* find all bboards */
  66.   tenexdos_subscribe,        /* subscribe to mailbox */
  67.   tenexdos_unsubscribe,        /* unsubscribe from mailbox */
  68.   tenexdos_subscribe_bboard,    /* subscribe to bboard */
  69.   tenexdos_subscribe_bboard,    /* unsubscribe (same as subscribe) */
  70.   tenexdos_create,        /* create mailbox */
  71.   tenexdos_delete,        /* delete mailbox */
  72.   tenexdos_rename,        /* rename mailbox */
  73.   tenexdos_open,        /* open mailbox */
  74.   tenexdos_close,        /* close mailbox */
  75.   tenexdos_fetchfast,        /* fetch message "fast" attributes */
  76.   tenexdos_fetchflags,        /* fetch message flags */
  77.   tenexdos_fetchstructure,    /* fetch message envelopes */
  78.   tenexdos_fetchheader,        /* fetch message header only */
  79.   tenexdos_fetchtext,        /* fetch message body only */
  80.   tenexdos_fetchbody,        /* fetch message body section */
  81.   tenexdos_setflag,        /* set message flag */
  82.   tenexdos_clearflag,        /* clear message flag */
  83.   tenexdos_search,        /* search for message based on criteria */
  84.   tenexdos_ping,        /* ping mailbox to see if still alive */
  85.   tenexdos_check,        /* check for new messages */
  86.   tenexdos_expunge,        /* expunge deleted messages */
  87.   tenexdos_copy,        /* copy messages to another mailbox */
  88.   tenexdos_move,        /* move messages to another mailbox */
  89.   tenexdos_append,        /* append string message to mailbox */
  90.   tenexdos_gc            /* garbage collect stream */
  91. };
  92.  
  93.                 /* prototype stream */
  94. MAILSTREAM tenexdosproto = {&tenexdosdriver};
  95.  
  96. /* Tenexdos mail validate mailbox
  97.  * Accepts: mailbox name
  98.  * Returns: our driver if name is valid, NIL otherwise
  99.  */
  100.  
  101. DRIVER *tenexdos_valid (char *name)
  102. {
  103.   return tenexdos_isvalid (name) ? &tenexdosdriver : (DRIVER *) NIL;
  104. }
  105.  
  106.  
  107. /* Tenexdos mail test for valid mailbox
  108.  * Accepts: mailbox name
  109.  * Returns: T if valid, NIL otherwise
  110.  */
  111.  
  112. long tenexdos_isvalid (char *name)
  113. {
  114.   int fd;
  115.   long ret = NIL;
  116.   char *s,tmp[MAILTMPLEN];
  117.   struct stat sbuf;
  118.   errno = EINVAL;        /* assume invalid argument */
  119.                 /* if file, get its status */
  120.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  121.       mailboxfile (tmp,name) && !stat (tmp,&sbuf)) {
  122.     if (!sbuf.st_size)errno = 0;/* empty file */
  123.     else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) {
  124.       memset (tmp,'\0',MAILTMPLEN);
  125.       if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) && 
  126.       (s[-1] != '\015')) {    /* valid format? */
  127.     *s = '\0';        /* tie off header */
  128.                 /* must begin with dd-mmm-yy" */
  129.     ret = (((tmp[2] == '-' && tmp[6] == '-') ||
  130.         (tmp[1] == '-' && tmp[5] == '-')) &&
  131.            (s = strchr (tmp+20,',')) && strchr (s+2,';')) ? T : NIL;
  132.       }
  133.       else errno = -1;        /* bogus format */
  134.       close (fd);        /* close the file */
  135.     }
  136.   }
  137.   return ret;            /* return what we should */
  138. }
  139.  
  140.  
  141. /* Tenexdos manipulate driver parameters
  142.  * Accepts: function code
  143.  *        function-dependent value
  144.  * Returns: function-dependent return value
  145.  */
  146.  
  147. void *tenexdos_parameters (long function,void *value)
  148. {
  149.   return NIL;
  150. }
  151.  
  152. /* Tenexdos mail find list of mailboxes
  153.  * Accepts: mail stream
  154.  *        pattern to search
  155.  */
  156.  
  157. void tenexdos_find (MAILSTREAM *stream,char *pat)
  158. {
  159.   if (stream) dummy_find (NIL,pat);
  160. }
  161.  
  162.  
  163. /* Tenexdos mail find list of bboards
  164.  * Accepts: mail stream
  165.  *        pattern to search
  166.  */
  167.  
  168. void tenexdos_find_bboards (MAILSTREAM *stream,char *pat)
  169. {
  170.   /* always a no-op */
  171. }
  172.  
  173.  
  174. /* Tenexdos mail find list of all mailboxes
  175.  * Accepts: mail stream
  176.  *        pattern to search
  177.  */
  178.  
  179. void tenexdos_find_all (MAILSTREAM *stream,char *pat)
  180. {
  181.   if (stream) dummy_find_all (NIL,pat);
  182. }
  183.  
  184. /* Tenexdos mail subscribe to mailbox
  185.  * Accepts: mail stream
  186.  *        mailbox to add to subscription list
  187.  * Returns: T on success, NIL on failure
  188.  */
  189.  
  190. long tenexdos_subscribe (MAILSTREAM *stream,char *mailbox)
  191. {
  192.   return dummy_subscribe (stream,mailbox);
  193. }
  194.  
  195.  
  196. /* Tenexdos mail unsubscribe to mailbox
  197.  * Accepts: mail stream
  198.  *        mailbox to delete from subscription list
  199.  * Returns: T on success, NIL on failure
  200.  */
  201.  
  202. long tenexdos_unsubscribe (MAILSTREAM *stream,char *mailbox)
  203. {
  204.   return dummy_unsubscribe (stream,mailbox);
  205. }
  206.  
  207.  
  208. /* Tenexdos mail subscribe to bboard
  209.  * Accepts: mail stream
  210.  *        bboard to add to subscription list
  211.  * Returns: T on success, NIL on failure
  212.  */
  213.  
  214. long tenexdos_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  215. {
  216.   return NIL;            /* never valid for Tenexdos */
  217. }
  218.  
  219. /* Tenexdos mail create mailbox
  220.  * Accepts: MAIL stream
  221.  *        mailbox name to create
  222.  * Returns: T on success, NIL on failure
  223.  */
  224.  
  225. long tenexdos_create (MAILSTREAM *stream,char *mailbox)
  226. {
  227.   return dummy_create (stream,mailbox);
  228. }
  229.  
  230.  
  231. /* Tenexdos mail delete mailbox
  232.  * Accepts: MAIL stream
  233.  *        mailbox name to delete
  234.  * Returns: T on success, NIL on failure
  235.  */
  236.  
  237. long tenexdos_delete (MAILSTREAM *stream,char *mailbox)
  238. {
  239.   return dummy_delete (stream,mailbox);
  240. }
  241.  
  242.  
  243. /* Tenexdos mail rename mailbox
  244.  * Accepts: MAIL stream
  245.  *        old mailbox name
  246.  *        new mailbox name (or NIL for delete)
  247.  * Returns: T on success, NIL on failure
  248.  */
  249.  
  250. long tenexdos_rename (MAILSTREAM *stream,char *old,char *new)
  251. {
  252.   return dummy_rename (stream,old,new);
  253. }
  254.  
  255. /* Tenexdos mail open
  256.  * Accepts: stream to open
  257.  * Returns: stream on success, NIL on failure
  258.  */
  259.  
  260. MAILSTREAM *tenexdos_open (MAILSTREAM *stream)
  261. {
  262.   long i;
  263.   int fd;
  264.   char *s;
  265.   char tmp[MAILTMPLEN];
  266.   struct stat sbuf;
  267.                 /* return prototype for OP_PROTOTYPE call */
  268.   if (!stream) return &tenexdosproto;
  269.   if (LOCAL) {            /* close old file if stream being recycled */
  270.     tenexdos_close (stream);    /* dump and save the changes */
  271.     stream->dtb = &tenexdosdriver;    /* reattach this driver */
  272.     mail_free_cache (stream);    /* clean up cache */
  273.   }
  274.   else {            /* flush flagstring and flags if any */
  275.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  276.     for (i = 0; i < NUSERFLAGS; ++i) stream->user_flags[i] = NIL;
  277.   }
  278.   if (!mailboxfile (tmp,stream->mailbox))
  279.     return (MAILSTREAM *) tenexdos_badname (tmp,stream->mailbox);
  280.   if (((fd = open (tmp,O_BINARY|(stream->rdonly ? O_RDONLY:O_RDWR),NIL))<0)){
  281.     sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  282.     mm_log (tmp,ERROR);
  283.     return NIL;
  284.   }
  285.   stream->local = fs_get (sizeof (TENEXDOSLOCAL));
  286.                 /* canonicalize the stream mailbox name */
  287.   fs_give ((void **) &stream->mailbox);
  288.   if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0';
  289.   stream->mailbox = cpystr (tmp);
  290.   LOCAL->fd = fd;        /* note the file */
  291.   LOCAL->filesize = 0;        /* initialize parsed file size */
  292.   stream->sequence++;        /* bump sequence number */
  293.                 /* parse mailbox */
  294.   stream->nmsgs = stream->recent = 0;
  295.   if (!tenexdos_ping (stream)) return NIL;
  296.   if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL);
  297.   return stream;        /* return stream to caller */
  298. }
  299.  
  300. /* Tenexdos mail close
  301.  * Accepts: MAIL stream
  302.  */
  303.  
  304. void tenexdos_close (MAILSTREAM *stream)
  305. {
  306.   long i;
  307.   if (stream && LOCAL) {    /* only if a file is open */
  308.     close (LOCAL->fd);        /* close the local file */
  309.                 /* nuke the local data */
  310.     fs_give ((void **) &stream->local);
  311.     stream->dtb = NIL;        /* log out the DTB */
  312.   }
  313. }
  314.  
  315.  
  316. /* Tenexdos mail fetch fast information
  317.  * Accepts: MAIL stream
  318.  *        sequence
  319.  */
  320.  
  321. void tenexdos_fetchfast (MAILSTREAM *stream,char *sequence)
  322. {
  323.   long i;
  324.                 /* make sure have RFC-822 size for messages */
  325.   if (stream && LOCAL && mail_sequence (stream,sequence))
  326.     for (i = 1; i <= stream->nmsgs; i++) tenexdos_822size (stream,i);
  327. }
  328.  
  329.  
  330. /* Tenexdos mail fetch flags
  331.  * Accepts: MAIL stream
  332.  *        sequence
  333.  */
  334.  
  335. void tenexdos_fetchflags (MAILSTREAM *stream,char *sequence)
  336. {
  337.   return;            /* no-op for local mail */
  338. }
  339.  
  340. /* Tenexdos string driver for file stringstructs */
  341.  
  342. STRINGDRIVER tenexdos_string = {
  343.   tenexdos_string_init,        /* initialize string structure */
  344.   tenexdos_string_next,        /* get next byte in string structure */
  345.   tenexdos_string_setpos    /* set position in string structure */
  346. };
  347.  
  348.  
  349. /* Cache buffer for file stringstructs */
  350.  
  351. #define DOSCHUNKLEN 4096
  352. char dos_chunk[DOSCHUNKLEN];
  353.  
  354.  
  355. /* Initialize tenexdos string structure for file stringstruct
  356.  * Accepts: string structure
  357.  *        pointer to string
  358.  *        size of string
  359.  */
  360.  
  361. void tenexdos_string_init (STRING *s,void *data,unsigned long size)
  362. {
  363.   TENEXDOSDATA *d = (TENEXDOSDATA *) data;
  364.   s->data = (void *) d->fd;    /* note fd */
  365.   s->data1 = d->pos;        /* note file offset */
  366.   s->size = size;        /* note size */
  367.   s->curpos = s->chunk = dos_chunk;
  368.   s->chunksize = (unsigned long) DOSCHUNKLEN;
  369.   s->offset = 0;        /* initial position */
  370.                 /* and size of data */
  371.   s->cursize = min (s->chunksize,size);
  372.                 /* move to that position in the file */
  373.   lseek (d->fd,d->pos,SEEK_SET);
  374.   read (d->fd,s->chunk,(unsigned int) s->cursize);
  375. }
  376.  
  377. /* Get next character from file stringstruct
  378.  * Accepts: string structure
  379.  * Returns: character, string structure chunk refreshed
  380.  */
  381.  
  382. char tenexdos_string_next (STRING *s)
  383. {
  384.   char c = *s->curpos++;    /* get next byte */
  385.                 /* move to next chunk */
  386.   SETPOS (s,s->offset + s->chunksize);
  387.   return c;            /* return the byte */
  388. }
  389.  
  390.  
  391. /* Set string pointer position for file stringstruct
  392.  * Accepts: string structure
  393.  *        new position
  394.  */
  395.  
  396. void tenexdos_string_setpos (STRING *s,unsigned long i)
  397. {
  398.   s->offset = i;        /* set new offset */
  399.   s->curpos = s->chunk;        /* reset position */
  400.                 /* set size of data */
  401.   if (s->cursize = s->size > s->offset ? min (s->chunksize,SIZE (s)) : 0) {
  402.                 /* move to that position in the file */
  403.     lseek ((int) s->data,s->data1 + s->offset,SEEK_SET);
  404.     read ((int) s->data,s->curpos,(unsigned int) s->cursize);
  405.   }
  406. }
  407.  
  408. /* Tenexdos mail fetch structure
  409.  * Accepts: MAIL stream
  410.  *        message # to fetch
  411.  *        pointer to return body
  412.  * Returns: envelope of this message, body returned in body value
  413.  *
  414.  * Fetches the "fast" information as well
  415.  */
  416.  
  417. #define MAXHDR (unsigned long) 4*MAILTMPLEN
  418.  
  419. ENVELOPE *tenexdos_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  420. {
  421.   LONGCACHE *lelt;
  422.   ENVELOPE **env;
  423.   BODY **b;
  424.   STRING bs;
  425.   TENEXDOSDATA d;
  426.   unsigned long hdrsize;
  427.   unsigned long hdrpos = tenexdos_header (stream,msgno,&hdrsize);
  428.   unsigned long textsize = body ? tenexdos_size (stream,msgno) - hdrsize : 0;
  429.                 /* make sure we have message size */
  430.   tenexdos_822size (stream,msgno);
  431.                 /* limit header size */
  432.   if (hdrsize > MAXHDR) hdrsize = MAXHDR;
  433.   if (stream->scache) {        /* short cache */
  434.     if (msgno != stream->msgno){/* flush old poop if a different message */
  435.       mail_free_envelope (&stream->env);
  436.       mail_free_body (&stream->body);
  437.     }
  438.     stream->msgno = msgno;
  439.     env = &stream->env;        /* get pointers to envelope and body */
  440.     b = &stream->body;
  441.   }
  442.   else {            /* long cache */
  443.     lelt = mail_lelt (stream,msgno);
  444.     env = &lelt->env;        /* get pointers to envelope and body */
  445.     b = &lelt->body;
  446.   }
  447.  
  448.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  449.     char *hdr = (char *) fs_get (hdrsize + 1);
  450.     char *tmp = (char *) fs_get (MAXHDR);
  451.     mail_free_envelope (env);    /* flush old envelope and body */
  452.     mail_free_body (b);
  453.                 /* get to header position */
  454.     lseek (LOCAL->fd,hdrpos,SEEK_SET);
  455.                 /* read the text */
  456.     if (read (LOCAL->fd,hdr,(unsigned int) hdrsize) >= 0) {
  457.       if (hdr[hdrsize-1] != '\012') hdr[hdrsize-1] = '\012';
  458.       hdr[hdrsize] = '\0';    /* make sure tied off */
  459.       d.fd = LOCAL->fd;        /* set initial stringstruct */
  460.       d.pos = hdrpos + hdrsize;
  461.       INIT (&bs,tenexdos_string,(void *) &d,textsize);
  462.                 /* parse envelope and body */
  463.       rfc822_parse_msg (env,body ? b : NIL,hdr,hdrsize,&bs,mylocalhost (),tmp);
  464.     }
  465.     fs_give ((void **) &tmp);
  466.     fs_give ((void **) &hdr);
  467.   }
  468.   if (body) *body = *b;        /* return the body */
  469.   return *env;            /* return the envelope */
  470. }
  471.  
  472. /* Tenexdos mail fetch message header
  473.  * Accepts: MAIL stream
  474.  *        message # to fetch
  475.  * Returns: message header in RFC822 format
  476.  */
  477.  
  478. char *tenexdos_fetchheader (MAILSTREAM *stream,long msgno)
  479. {
  480.   unsigned long hdrsize;
  481.   unsigned long hdrpos = tenexdos_header (stream,msgno,&hdrsize);
  482.   if (stream->text) fs_give ((void **) &stream->text);
  483.   return stream->text = tenexdos_slurp (stream,hdrpos,&hdrsize);
  484. }
  485.  
  486.  
  487. /* Tenexdos mail fetch message text (body only)
  488.  * Accepts: MAIL stream
  489.  *        message # to fetch
  490.  * Returns: message text in RFC822 format
  491.  */
  492.  
  493. char *tenexdos_fetchtext (MAILSTREAM *stream,long msgno)
  494. {
  495.   unsigned long hdrsize;
  496.   unsigned long hdrpos = tenexdos_header (stream,msgno,&hdrsize);
  497.   unsigned long textsize = tenexdos_size (stream,msgno) - hdrsize;
  498.   if (stream->text) fs_give ((void **) &stream->text);
  499.                 /* mark message as seen */
  500.   mail_elt (stream,msgno)->seen = T;
  501.                 /* recalculate status */
  502.   tenexdos_update_status (stream,msgno);
  503.   return stream->text = tenexdos_slurp (stream,hdrpos + hdrsize,&textsize);
  504. }
  505.  
  506. /* Tenexdos fetch message body as a structure
  507.  * Accepts: Mail stream
  508.  *        message # to fetch
  509.  *        section specifier
  510.  *        pointer to length
  511.  * Returns: pointer to section of message body
  512.  */
  513.  
  514. char *tenexdos_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  515. {
  516.   BODY *b;
  517.   PART *pt;
  518.   unsigned long i;
  519.   unsigned long base;
  520.   unsigned long offset = 0;
  521.   unsigned long hdrpos = tenexdos_header (stream,m,&base);
  522.   MESSAGECACHE *elt = mail_elt (stream,m);
  523.   if (stream->text) fs_give ((void **) &stream->text);
  524.                 /* make sure have a body */
  525.   if (!(tenexdos_fetchstructure (stream,m,&b) && b && s && *s &&
  526.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  527.   do {                /* until find desired body part */
  528.                 /* multipart content? */
  529.     if (b->type == TYPEMULTIPART) {
  530.       pt = b->contents.part;    /* yes, find desired part */
  531.       while (--i && (pt = pt->next));
  532.       if (!pt) return NIL;    /* bad specifier */
  533.                 /* note new body, check valid nesting */
  534.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  535.       offset = pt->offset;    /* get new offset */
  536.     }
  537.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  538.                 /* need to go down further? */
  539.     if (i = *s) switch (b->type) {
  540.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  541.       offset = b->contents.msg.offset;
  542.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  543.     case TYPEMULTIPART:        /* multipart, get next section */
  544.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  545.     default:            /* bogus subpart specification */
  546.       return NIL;
  547.     }
  548.   } while (i);
  549.                 /* lose if body bogus */
  550.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  551.   elt->seen = T;        /* mark message as seen */
  552.   tenexdos_update_status (stream,m);/* recalculate status */
  553.   *len = b->size.bytes;        /* number of bytes from file */
  554.   return stream->text = tenexdos_slurp (stream,hdrpos + base + offset,len);
  555. }
  556.  
  557. /* Tenexdos mail slurp
  558.  * Accepts: MAIL stream
  559.  *        file position
  560.  *        pointer to number of file bytes to read
  561.  * Returns: buffer address, actual number of bytes written
  562.  */
  563.  
  564. char *tenexdos_slurp (MAILSTREAM *stream,unsigned long pos,
  565.               unsigned long *count)
  566. {
  567.   unsigned long cnt = *count;
  568.   int i,j;
  569.   char tmp[MAILTMPLEN];
  570.   mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
  571.   lseek(LOCAL->fd,pos,SEEK_SET);/* get to desired position */
  572.   LOCAL->ch = '\0';        /* initialize CR mechanism */
  573.   while (cnt) {            /* until checked all bytes */
  574.                 /* number of bytes this chunk */
  575.     cnt -= (i = (int) min (cnt,(unsigned long) MAILTMPLEN));
  576.                 /* read a chunk */
  577.     if (read (LOCAL->fd,tmp,i) != i) return NIL;
  578.     for (j = 0; j < i; j++) {    /* count bytes in chunk */
  579.                 /* if see bare LF, count next as CR */
  580.       if ((tmp[j] == '\012') && (LOCAL->ch != '\015')) ++*count;
  581.       LOCAL->ch = tmp[j];
  582.     }
  583.   }
  584.   lseek(LOCAL->fd,pos,SEEK_SET);/* get to desired position */
  585.   LOCAL->ch = '\0';        /* initialize CR mechanism */
  586.   return (mg ? *mg : mm_gets) (tenexdos_read,stream,*count);
  587. }
  588.  
  589.  
  590. /* Tenexdos mail read
  591.  * Accepts: MAIL stream
  592.  *        number of bytes to read
  593.  *        buffer address
  594.  * Returns: T if success, NIL otherwise
  595.  */
  596.  
  597. long tenexdos_read (MAILSTREAM *stream,unsigned long count,char *buffer)
  598. {
  599.   char tmp[MAILTMPLEN];
  600.   int i,j;
  601.   while (count) {        /* until no more bytes to do */
  602.                 /* read a chunk */
  603.     if ((i = read (LOCAL->fd,tmp,(int) min (count,(unsigned long) MAILTMPLEN)))
  604.     < 0) return NIL;
  605.                 /* for each byte in chunk (or filled) */
  606.     for (j = 0; count && (j < i); count--) {
  607.                 /* if see LF, insert CR unless already there */
  608.       if ((tmp[j] == '\012') && (LOCAL->ch != '\015')) LOCAL->ch = '\015';
  609.       else LOCAL->ch = tmp[j++];/* regular character */
  610.       *buffer++ = LOCAL->ch;    /* poop in buffer */
  611.     }
  612.     if (i -= j) lseek (LOCAL->fd,-((long) i),SEEK_CUR);
  613.   }
  614.   return T;
  615. }
  616.  
  617. /* Tenexdos locate header for a message
  618.  * Accepts: MAIL stream
  619.  *        message number
  620.  *        pointer to returned header size
  621.  * Returns: position of header in file
  622.  */
  623.  
  624. unsigned long tenexdos_header (MAILSTREAM *stream,long msgno,
  625.                    unsigned long *size)
  626. {
  627.   long siz;
  628.   long i = 0;
  629.   char c = '\0';
  630.   char *s;
  631.   char tmp[MAILTMPLEN];
  632.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  633.   long pos = elt->data1 + (elt->data2 >> 24);
  634.   long msiz = tenexdos_size (stream,msgno);
  635.                 /* is size known? */
  636.   if (!(*size = (elt->data2 & (unsigned long) 0xffffff))) {
  637.                 /* get to header position */
  638.     lseek (LOCAL->fd,pos,SEEK_SET);
  639.                 /* search message for CRLF CRLF */
  640.     for (siz = 0; siz < msiz; siz++) {
  641.                 /* read another buffer as necessary */
  642.       if (--i <= 0)        /* buffer empty? */
  643.     if (read (LOCAL->fd,s = tmp,
  644.           i = min (msiz-siz,(unsigned long) MAILTMPLEN)) < 0)
  645.       return pos;        /* I/O error? */
  646.                 /* two newline sequence? */
  647.       if ((c == '\012') && (*s == '\012')) {
  648.                 /* yes, note for later */
  649.     elt->data2 |= (*size = siz + 1);
  650.     return pos;        /* return to caller */
  651.       }
  652.       else c = *s++;        /* next character */
  653.  
  654.     }
  655.   }
  656.   return pos;            /* have position */
  657. }
  658.  
  659. /* Tenexdos mail set flag
  660.  * Accepts: MAIL stream
  661.  *        sequence
  662.  *        flag(s)
  663.  */
  664.  
  665. void tenexdos_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  666. {
  667.   MESSAGECACHE *elt;
  668.   long i;
  669.   short f = tenexdos_getflags (stream,flag);
  670.   if (!f) return;        /* no-op if no flags to modify */
  671.                 /* get sequence and loop on it */
  672.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  673.     if ((elt = mail_elt (stream,i))->sequence) {
  674.                 /* set all requested flags */
  675.       if (f&fSEEN) elt->seen = T;
  676.       if (f&fDELETED) elt->deleted = T;
  677.       if (f&fFLAGGED) elt->flagged = T;
  678.       if (f&fANSWERED) elt->answered = T;
  679.                 /* recalculate status */
  680.       tenexdos_update_status (stream,i);
  681.     }
  682. }
  683.  
  684. /* Tenexdos mail clear flag
  685.  * Accepts: MAIL stream
  686.  *        sequence
  687.  *        flag(s)
  688.  */
  689.  
  690. void tenexdos_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  691. {
  692.   MESSAGECACHE *elt;
  693.   long i;
  694.   short f = tenexdos_getflags (stream,flag);
  695.   if (!f) return;        /* no-op if no flags to modify */
  696.                 /* get sequence and loop on it */
  697.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  698.     if ((elt = mail_elt (stream,i))->sequence) {
  699.                 /* clear all requested flags */
  700.       if (f&fSEEN) elt->seen = NIL;
  701.       if (f&fDELETED) elt->deleted = NIL;
  702.       if (f&fFLAGGED) elt->flagged = NIL;
  703.       if (f&fANSWERED) elt->answered = NIL;
  704.                 /* recalculate status */
  705.       tenexdos_update_status (stream,i);
  706.     }
  707. }
  708.  
  709. /* Tenexdos mail search for messages
  710.  * Accepts: MAIL stream
  711.  *        search criteria
  712.  */
  713.  
  714. void tenexdos_search (MAILSTREAM *stream,char *criteria)
  715. {
  716.   long i,n;
  717.   char *d;
  718.   search_t f;
  719.                 /* initially all searched */
  720.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  721.                 /* get first criterion */
  722.   if (criteria && (criteria = strtok (criteria," "))) {
  723.                 /* for each criterion */
  724.     for (; criteria; (criteria = strtok (NIL," "))) {
  725.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  726.       switch (*ucase (criteria)) {
  727.       case 'A':            /* possible ALL, ANSWERED */
  728.     if (!strcmp (criteria+1,"LL")) f = tenexdos_search_all;
  729.     else if (!strcmp (criteria+1,"NSWERED")) f = tenexdos_search_answered;
  730.     break;
  731.       case 'B':            /* possible BCC, BEFORE, BODY */
  732.     if (!strcmp (criteria+1,"CC"))
  733.       f = tenexdos_search_string (tenexdos_search_bcc,&d,&n);
  734.     else if (!strcmp (criteria+1,"EFORE"))
  735.       f = tenexdos_search_date (tenexdos_search_before,&n);
  736.     else if (!strcmp (criteria+1,"ODY"))
  737.       f = tenexdos_search_string (tenexdos_search_body,&d,&n);
  738.     break;
  739.       case 'C':            /* possible CC */
  740.     if (!strcmp (criteria+1,"C")) 
  741.       f = tenexdos_search_string (tenexdos_search_cc,&d,&n);
  742.     break;
  743.       case 'D':            /* possible DELETED */
  744.     if (!strcmp (criteria+1,"ELETED")) f = tenexdos_search_deleted;
  745.     break;
  746.       case 'F':            /* possible FLAGGED, FROM */
  747.     if (!strcmp (criteria+1,"LAGGED")) f = tenexdos_search_flagged;
  748.     else if (!strcmp (criteria+1,"ROM"))
  749.       f = tenexdos_search_string (tenexdos_search_from,&d,&n);
  750.     break;
  751.       case 'K':            /* possible KEYWORD */
  752.     if (!strcmp (criteria+1,"EYWORD"))
  753.       f = tenexdos_search_flag (tenexdos_search_keyword,&n,stream);
  754.     break;
  755.       case 'N':            /* possible NEW */
  756.     if (!strcmp (criteria+1,"EW")) f = tenexdos_search_new;
  757.     break;
  758.  
  759.       case 'O':            /* possible OLD, ON */
  760.     if (!strcmp (criteria+1,"LD")) f = tenexdos_search_old;
  761.     else if (!strcmp (criteria+1,"N"))
  762.       f = tenexdos_search_date (tenexdos_search_on,&n);
  763.     break;
  764.       case 'R':            /* possible RECENT */
  765.     if (!strcmp (criteria+1,"ECENT")) f = tenexdos_search_recent;
  766.     break;
  767.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  768.     if (!strcmp (criteria+1,"EEN")) f = tenexdos_search_seen;
  769.     else if (!strcmp (criteria+1,"INCE"))
  770.       f = tenexdos_search_date (tenexdos_search_since,&n);
  771.     else if (!strcmp (criteria+1,"UBJECT"))
  772.       f = tenexdos_search_string (tenexdos_search_subject,&d,&n);
  773.     break;
  774.       case 'T':            /* possible TEXT, TO */
  775.     if (!strcmp (criteria+1,"EXT"))
  776.       f = tenexdos_search_string (tenexdos_search_text,&d,&n);
  777.     else if (!strcmp (criteria+1,"O"))
  778.       f = tenexdos_search_string (tenexdos_search_to,&d,&n);
  779.     break;
  780.       case 'U':            /* possible UN* */
  781.     if (criteria[1] == 'N') {
  782.       if (!strcmp (criteria+2,"ANSWERED")) f = tenexdos_search_unanswered;
  783.       else if (!strcmp (criteria+2,"DELETED"))
  784.         f = tenexdos_search_undeleted;
  785.       else if (!strcmp (criteria+2,"FLAGGED"))
  786.         f = tenexdos_search_unflagged;
  787.       else if (!strcmp (criteria+2,"KEYWORD"))
  788.         f = tenexdos_search_flag (tenexdos_search_unkeyword,&n,stream);
  789.       else if (!strcmp (criteria+2,"SEEN")) f = tenexdos_search_unseen;
  790.     }
  791.     break;
  792.       default:            /* we will barf below */
  793.     break;
  794.       }
  795.       if (!f) {            /* if can't determine any criteria */
  796.     mm_log ("Unknown search criterion",ERROR);
  797.     return;
  798.       }
  799.                 /* run the search criterion */
  800.       for (i = 1; i <= stream->nmsgs; ++i)
  801.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  802.       mail_elt (stream,i)->searched = NIL;
  803.     }
  804.                 /* report search results to main program */
  805.     for (i = 1; i <= stream->nmsgs; ++i)
  806.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  807.   }
  808. }
  809.  
  810. /* Tenexdos mail ping mailbox
  811.  * Accepts: MAIL stream
  812.  * Returns: T if stream still alive, NIL if not
  813.  */
  814.  
  815. long tenexdos_ping (MAILSTREAM *stream)
  816. {
  817.   long i = 0;
  818.   long r,j;
  819.   struct stat sbuf;
  820.                 /* punt if stream no longer alive */
  821.   if (!(stream && LOCAL)) return NIL;
  822.                 /* parse mailbox, punt if parse dies */
  823.   return (tenexdos_parse (stream)) ? T : NIL;
  824. }
  825.  
  826.  
  827. /* Tenexdos mail check mailbox (reparses status too)
  828.  * Accepts: MAIL stream
  829.  */
  830.  
  831. void tenexdos_check (MAILSTREAM *stream)
  832. {
  833.   long i = 1;
  834.   if (tenexdos_ping (stream)) {    /* ping mailbox */
  835.                 /* get new message status */
  836.     while (i <= stream->nmsgs) mail_elt (stream,i++);
  837.     mm_log ("Check completed",(long) NIL);
  838.   }
  839. }
  840.  
  841. /* Tenexdos mail expunge mailbox
  842.  * Accepts: MAIL stream
  843.  */
  844.  
  845. void tenexdos_expunge (MAILSTREAM *stream)
  846. {
  847.   unsigned long i = 1;
  848.   unsigned long j,k,m,recent;
  849.   unsigned long n = 0;
  850.   unsigned long delta = 0;
  851.   MESSAGECACHE *elt;
  852.   char tmp[MAILTMPLEN];
  853.                 /* do nothing if stream dead */
  854.   if (!tenexdos_ping (stream)) return;
  855.   if (stream->rdonly) {        /* won't do on readonly files! */
  856.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  857.     return;
  858.   }
  859.   mm_critical (stream);        /* go critical */
  860.   recent = stream->recent;    /* get recent now that pinged */ 
  861.   while (i <= stream->nmsgs) {    /* for each message */
  862.                 /* if deleted */
  863.     if ((elt = mail_elt (stream,i))->deleted) {
  864.       if (elt->recent) --recent;/* if recent, note one less recent message */
  865.                 /* number of bytes to delete */
  866.       delta += (elt->data2 >> 24) + tenexdos_size (stream,i);
  867.       mail_expunged (stream,i);    /* notify upper levels */
  868.       n++;            /* count up one more deleted message */
  869.     }
  870.     else if (i++ && delta) {    /* preserved message */
  871.       j = elt->data1;        /* j is byte to copy, k is number of bytes */
  872.       k = (elt->data2 >> 24) + tenexdos_size (stream,i);
  873.       do {            /* read from source position */
  874.     m = min (k,(unsigned long) MAILTMPLEN);
  875.     lseek (LOCAL->fd,j,SEEK_SET);
  876.     read (LOCAL->fd,tmp,(unsigned int) m);
  877.                 /* write to destination position */
  878.     lseek (LOCAL->fd,j - delta,SEEK_SET);
  879.     write (LOCAL->fd,tmp,(unsigned int) m);
  880.     j += m;            /* next chunk, perhaps */
  881.       } while (k -= m);        /* until done */
  882.       elt->data1 -= delta;    /* note the new address of this text */
  883.     }
  884.   }
  885.   if (n) {            /* truncate file after last message */
  886.     chsize (LOCAL->fd,LOCAL->filesize -= delta);
  887.     sprintf (tmp,"Expunged %ld messages",n);
  888.     mm_log (tmp,(long) NIL);    /* output the news */
  889.   }
  890.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  891.   mm_nocritical (stream);    /* release critical */
  892.                 /* notify upper level of new mailbox size */
  893.   mail_exists (stream,stream->nmsgs);
  894.   mail_recent (stream,recent);
  895. }
  896.  
  897. /* Tenexdos mail copy message(s)
  898.  * Accepts: MAIL stream
  899.  *        sequence
  900.  *        destination mailbox
  901.  * Returns: T if success, NIL if failed
  902.  */
  903.  
  904. long tenexdos_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  905. {
  906.                 /* copy the messages */
  907.   return (mail_sequence (stream,sequence)) ?
  908.     tenexdos_copy_messages (stream,mailbox) : NIL;
  909. }
  910.  
  911.  
  912. /* Tenexdos mail move message(s)
  913.  * Accepts: MAIL stream
  914.  *        sequence
  915.  *        destination mailbox
  916.  * Returns: T if success, NIL if failed
  917.  */
  918.  
  919. long tenexdos_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  920. {
  921.   long i;
  922.   MESSAGECACHE *elt;
  923.   if (!(mail_sequence (stream,sequence) &&
  924.     tenexdos_copy_messages (stream,mailbox))) return NIL;
  925.                 /* delete all requested messages */
  926.   for (i = 1; i <= stream->nmsgs; i++)
  927.     if ((elt = mail_elt (stream,i))->sequence) {
  928.       elt->deleted = T;        /* mark message deleted */
  929.                 /* recalculate status */
  930.       tenexdos_update_status (stream,i);
  931.     }
  932.   return T;
  933. }
  934.  
  935. /* Tenexdos mail append message from stringstruct
  936.  * Accepts: MAIL stream
  937.  *        destination mailbox
  938.  *        stringstruct of messages to append
  939.  * Returns: T if append successful, else NIL
  940.  */
  941.  
  942. long tenexdos_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  943.           STRING *message)
  944. {
  945.   struct stat sbuf;
  946.   int fd,zone;
  947.   char c,tmp[MAILTMPLEN];
  948.   MESSAGECACHE elt;
  949.   long i = SIZE (message);
  950.   long size;
  951.   short f = tenexdos_getflags (stream,flags);
  952.   if (date) {            /* want to preserve date? */
  953.                 /* yes, parse date into an elt */
  954.     if (!mail_parse_date (&elt,date)) {
  955.       mm_log ("Bad date in append",ERROR);
  956.       return NIL;
  957.     }
  958.   }
  959.                 /* make sure valid mailbox */
  960.   if (!tenexdos_isvalid (mailbox) && errno) {
  961.     if (errno == ENOENT)
  962.       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
  963.          (long) NIL);
  964.     else if (mailboxfile (tmp,mailbox)) {
  965.       sprintf (tmp,"Not a Tenexdos-format mailbox: %s",mailbox);
  966.       mm_log (tmp,ERROR);
  967.     }
  968.     else tenexdos_badname (tmp,mailbox);
  969.     return NIL;
  970.   }
  971.                 /* open the destination */
  972.   if ((fd = open (mailboxfile (tmp,mailbox),
  973.           O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
  974.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  975.     mm_log (tmp,ERROR);
  976.     return NIL;
  977.   }
  978.   mm_critical (stream);        /* go critical */
  979.                 /* calculate data size w/o CR's */
  980.   while (i--) if ((c = SNX (message)) != '\015') size++;
  981.   SETPOS (message,(long) 0);    /* back to start */
  982.   fstat (fd,&sbuf);        /* get current file size */
  983.   if (date) mail_date(tmp,&elt);/* use date if given */
  984.   else internal_date (tmp);    /* else use time now */
  985.                 /* add remainder of header */
  986.   sprintf (tmp + strlen (tmp),",%ld;0000000000%02o\012",size,f);
  987.  
  988.                 /* write header */
  989.   if (write (fd,tmp,strlen (tmp)) < 0) {
  990.     sprintf (tmp,"Header write failed: %s",strerror (errno));
  991.     mm_log (tmp,ERROR);
  992.     chsize (fd,sbuf.st_size);
  993.   }  
  994.   else while (size) {        /* while there is more data to write */
  995.     for (i = 0; (i < MAILTMPLEN) && SIZE (message); )
  996.       if ((c = SNX (message)) != '\015') tmp[i++] = c;
  997.     if (write (fd,tmp,i) < 0) {    /* write another buffer's worth */
  998.       sprintf (tmp,"Message append failed: %s",strerror (errno));
  999.       mm_log (tmp,ERROR);
  1000.       chsize (fd,sbuf.st_size);
  1001.       break;
  1002.     }
  1003.     size -= i;            /* note that we wrote out this much */
  1004.   }
  1005.   close (fd);            /* close the file */
  1006.   mm_nocritical (stream);    /* release critical */
  1007.   return T;            /* return success */
  1008. }
  1009.  
  1010.  
  1011. /* Tenexdos garbage collect stream
  1012.  * Accepts: Mail stream
  1013.  *        garbage collection flags
  1014.  */
  1015.  
  1016. void tenexdos_gc (MAILSTREAM *stream,long gcflags)
  1017. {
  1018.   /* nothing here for now */
  1019. }
  1020.  
  1021. /* Internal routines */
  1022.  
  1023.  
  1024. /* Tenexdos mail return internal message size in bytes
  1025.  * Accepts: MAIL stream
  1026.  *        message #
  1027.  * Returns: internal size of message
  1028.  */
  1029.  
  1030. unsigned long tenexdos_size (MAILSTREAM *stream,long m)
  1031. {
  1032.   unsigned long end = (m < stream->nmsgs) ?
  1033.     mail_elt (stream,m+1)->data1 : LOCAL->filesize;
  1034.   MESSAGECACHE *elt = mail_elt (stream,m);
  1035.   return end - (elt->data1 + (elt->data2 >> 24));
  1036. }
  1037.  
  1038.  
  1039. /* Tenexdos mail return RFC-822 size in bytes
  1040.  * Accepts: MAIL stream
  1041.  *        message #
  1042.  * Returns: message size
  1043.  */
  1044.  
  1045. unsigned long tenexdos_822size (MAILSTREAM *stream,long msgno)
  1046. {
  1047.   unsigned long i,hdrpos,msgsize;
  1048.   char tmp[MAILTMPLEN];
  1049.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1050.   if (!elt->rfc822_size) {    /* have header size yet? */
  1051.                 /* no, get header position and size */
  1052.     hdrpos = tenexdos_header (stream,msgno,&msgsize);
  1053.     elt->rfc822_size = msgsize = tenexdos_size (stream,msgno);
  1054.                 /* get to header position */
  1055.     lseek (LOCAL->fd,hdrpos,SEEK_SET);
  1056.     while (msgsize) {        /* read message */
  1057.       read (LOCAL->fd,tmp,i = min (msgsize,(long) MAILTMPLEN));
  1058.       msgsize -= i;        /* account for having read that much */
  1059.                 /* now count the newlines */
  1060.       while (i) if ((tmp[--i] == '\n') && (!i || (tmp[i-1] != '\015')))
  1061.     elt->rfc822_size++;
  1062.     }
  1063.   }
  1064.   return elt->rfc822_size;
  1065. }
  1066.  
  1067.  
  1068. /* Return bad file name error message
  1069.  * Accepts: temporary buffer
  1070.  *        file name
  1071.  * Returns: long NIL always
  1072.  */
  1073.  
  1074. long tenexdos_badname (char *tmp,char *s)
  1075. {
  1076.   sprintf (tmp,"Invalid mailbox name: %s",s);
  1077.   mm_log (tmp,ERROR);
  1078.   return (long) NIL;
  1079. }
  1080.  
  1081. /* Parse flag list
  1082.  * Accepts: MAIL stream
  1083.  *        flag list as a character string
  1084.  * Returns: system flags
  1085.  */
  1086.  
  1087. long tenexdos_getflags (MAILSTREAM *stream,char *flag)
  1088. {
  1089.   char tmp[MAILTMPLEN];
  1090.   char key[MAILTMPLEN];
  1091.   char *t,*s;
  1092.   short f = 0;
  1093.   long i;
  1094.   short j;
  1095.   if (flag && *flag) {        /* no-op if no flag string */
  1096.                 /* check if a list and make sure valid */
  1097.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1098.       mm_log ("Bad flag list",ERROR);
  1099.       return NIL;
  1100.     }
  1101.                 /* copy the flag string w/o list construct */
  1102.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1103.     tmp[j] = '\0';        /* tie off tail */
  1104.  
  1105.                 /* make uppercase, find first, parse */
  1106.     if (t = strtok (ucase (tmp)," ")) do {
  1107.       i = 0;            /* no flag yet */
  1108.                 /* system flag, dispatch on first character */
  1109.       if (*t == '\\') switch (*++t) {
  1110.       case 'S':            /* possible \Seen flag */
  1111.     if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
  1112.       f |= i = fSEEN;
  1113.     break;
  1114.       case 'D':            /* possible \Deleted flag */
  1115.     if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1116.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
  1117.     break;
  1118.       case 'F':            /* possible \Flagged flag */
  1119.     if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1120.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
  1121.     break;
  1122.       case 'A':            /* possible \Answered flag */
  1123.     if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1124.         t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
  1125.       f |= i = fANSWERED;
  1126.     break;
  1127.       default:            /* unknown */
  1128.     break;
  1129.       }
  1130.       if (!i) {            /* didn't find a matching flag? */
  1131.     sprintf (key,"Unknown flag: %.80s",t);
  1132.     mm_log (key,ERROR);
  1133.       }
  1134.                 /* parse next flag */
  1135.     } while (t = strtok (NIL," "));
  1136.   }
  1137.   return f;
  1138. }
  1139.  
  1140. /* Tenexdos mail parse mailbox
  1141.  * Accepts: MAIL stream
  1142.  * Returns: T if parse OK
  1143.  *        NIL if failure, stream aborted
  1144.  */
  1145.  
  1146. long tenexdos_parse (MAILSTREAM *stream)
  1147. {
  1148.   struct stat sbuf;
  1149.   MESSAGECACHE *elt = NIL;
  1150.   char c,*s,*t,*x;
  1151.   char lbuf[65],tmp[MAILTMPLEN];
  1152.   int j;
  1153.   long i,msiz;
  1154.   long curpos = LOCAL->filesize;
  1155.   long nmsgs = stream->nmsgs;
  1156.   long recent = stream->recent;
  1157.   fstat (LOCAL->fd,&sbuf);    /* get status */
  1158.   if (sbuf.st_size < curpos) {    /* sanity check */
  1159.     sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
  1160.     mm_log (tmp,ERROR);
  1161.     tenexdos_close (stream);
  1162.     return NIL;
  1163.   }
  1164.   while (sbuf.st_size - curpos){/* while there is stuff to parse */
  1165.                 /* get to that position in the file */
  1166.     lseek (LOCAL->fd,curpos,SEEK_SET);
  1167.     if ((i = read (LOCAL->fd,lbuf,64)) <= 0) {
  1168.       sprintf (tmp,"Unable to read internal header at %ld, size = %ld: %s",
  1169.            curpos,sbuf.st_size,i ? strerror (errno) : "no data read");
  1170.       mm_log (tmp,ERROR);
  1171.       tenexdos_close (stream);
  1172.       return NIL;
  1173.     }
  1174.     lbuf[i] = '\0';        /* tie off buffer just in case */
  1175.     if (!(s = strchr (lbuf,'\012'))) {
  1176.       sprintf (tmp,"Unable to find end of line at %ld in %ld bytes, text: %s",
  1177.            curpos,i,lbuf);
  1178.       mm_log (tmp,ERROR);
  1179.       tenexdos_close (stream);
  1180.       return NIL;
  1181.     }
  1182.     *s = '\0';            /* tie off header line */
  1183.     i = (s + 1) - lbuf;        /* note start of text offset */
  1184.     if (!((s = strchr (lbuf,',')) && (t = strchr (s+1,';')))) {
  1185.       sprintf (tmp,"Unable to parse internal header at %ld: %s",curpos,lbuf);
  1186.       mm_log (tmp,ERROR);
  1187.       tenexdos_close (stream);
  1188.       return NIL;
  1189.     }
  1190.  
  1191.     *s++ = '\0'; *t++ = '\0';    /* tie off fields */
  1192.                 /* intantiate an elt for this message */
  1193.     (elt = mail_elt (stream,++nmsgs))->valid = T;
  1194.     elt->data1 = curpos;    /* note file offset of header */
  1195.     elt->data2 = i << 24;    /* as well as offset from header of message */
  1196.                 /* parse the header components */
  1197.     if (!(mail_parse_date (elt,lbuf) &&
  1198.       (msiz = strtol (x = s,&s,10)) && (!(s && *s)) &&
  1199.       isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
  1200.       isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
  1201.       isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
  1202.       isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
  1203.       sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
  1204.            curpos,lbuf,x,t);
  1205.       tenexdos_close (stream);
  1206.       return NIL;
  1207.     }
  1208.                 /* start at first message byte */
  1209.     lseek (LOCAL->fd,curpos + i,SEEK_SET);
  1210.                 /* make sure didn't run off end of file */
  1211.     if ((curpos += (msiz + i)) > sbuf.st_size) {
  1212.       mm_log ("Last message runs past end of file",ERROR);
  1213.       tenexdos_close (stream);
  1214.       return NIL;
  1215.     }
  1216.                 /* calculate system flags */
  1217.     if ((i = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
  1218.     if (i & fDELETED) elt->deleted = T;
  1219.     if (i & fFLAGGED) elt->flagged = T;
  1220.     if (i & fANSWERED) elt->answered = T;
  1221.   }
  1222.                 /* update parsed file size */
  1223.   LOCAL->filesize = sbuf.st_size;
  1224.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1225.   mail_recent (stream,recent);    /* and of change in recent messages */
  1226.   return T;            /* return the winnage */
  1227. }
  1228.  
  1229. /* Tenexdos copy messages
  1230.  * Accepts: MAIL stream
  1231.  *        mailbox copy vector
  1232.  *        mailbox name
  1233.  * Returns: T if success, NIL if failed
  1234.  */
  1235.  
  1236. long tenexdos_copy_messages (MAILSTREAM *stream,char *mailbox)
  1237. {
  1238.   char tmp[MAILTMPLEN];
  1239.   struct stat sbuf;
  1240.   MESSAGECACHE *elt;
  1241.   unsigned long i,j,k;
  1242.   long ret = LONGT;
  1243.   int fd;
  1244.                 /* make sure valid mailbox */
  1245.   if (!tenexdos_isvalid (mailbox) && errno) {
  1246.     if (errno == ENOENT)
  1247.       mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
  1248.          (long) NIL);
  1249.     else if (mailboxfile (tmp,mailbox)) {
  1250.       sprintf (tmp,"Not a Tenexdos-format mailbox: %s",mailbox);
  1251.       mm_log (tmp,ERROR);
  1252.     }
  1253.     else tenexdos_badname (tmp,mailbox);
  1254.     return NIL;
  1255.   }
  1256.                 /* open the destination */
  1257.   if ((fd = open (mailboxfile (tmp,mailbox),
  1258.           O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
  1259.     sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
  1260.     mm_log (tmp,ERROR);
  1261.     return NIL;
  1262.   }
  1263.   mm_critical (stream);        /* go critical */
  1264.   fstat (fd,&sbuf);        /* get current file size */
  1265.                 /* for each requested message */
  1266.   for (i = 1; ret && (i <= stream->nmsgs); i++) 
  1267.     if ((elt = mail_elt (stream,i))->sequence) {
  1268.       lseek (LOCAL->fd,elt->data1,SEEK_SET);
  1269.                 /* number of bytes to copy */
  1270.       k = (elt->data2 >> 24) + tenexdos_size (stream,i);
  1271.       do {            /* read from source position */
  1272.     j = min (k,(long) MAILTMPLEN);
  1273.     read (LOCAL->fd,tmp,(unsigned int) j);
  1274.     if (write (fd,tmp,(unsigned int) j) < 0) {
  1275.       sprintf (tmp,"Unable to write message: %s",strerror (errno));
  1276.       mm_log (tmp,ERROR);
  1277.       chsize (fd,sbuf.st_size);
  1278.       j = k;
  1279.       ret = NIL;        /* note error */
  1280.       break;
  1281.     }
  1282.       } while (k -= j);        /* until done */
  1283.     }
  1284.   close (fd);            /* close the file */
  1285.   mm_nocritical (stream);    /* release critical */
  1286.   return ret;
  1287. }
  1288.  
  1289. /* Tenexdos update status string
  1290.  * Accepts: MAIL stream
  1291.  *        message number
  1292.  */
  1293.  
  1294. void tenexdos_update_status (MAILSTREAM *stream,long msgno)
  1295. {
  1296.   char tmp[MAILTMPLEN];
  1297.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1298.   unsigned long j,k = 0;
  1299.   if (stream->rdonly) return;    /* not if readonly you don't */
  1300.   j = elt->user_flags;        /* get user flags */
  1301.                 /* reverse bits (dontcha wish we had CIRC?) */
  1302.   while (j) k |= 1 << 29 - find_rightmost_bit (&j);
  1303.                 /* print new flag string */
  1304.   sprintf (tmp,"%010lo%02o",k,(fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1305.        (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  1306.                 /* get to that place in the file */
  1307.   lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 13,SEEK_SET);
  1308.   write (LOCAL->fd,tmp,12);    /* write new flags */
  1309. }
  1310.  
  1311. /* Search support routines
  1312.  * Accepts: MAIL stream
  1313.  *        message number
  1314.  *        pointer to additional data
  1315.  * Returns: T if search matches, else NIL
  1316.  */
  1317.  
  1318.  
  1319. char tenexdos_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  1320. {
  1321.   return T;            /* ALL always succeeds */
  1322. }
  1323.  
  1324.  
  1325. char tenexdos_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  1326. {
  1327.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1328. }
  1329.  
  1330.  
  1331. char tenexdos_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1332. {
  1333.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1334. }
  1335.  
  1336.  
  1337. char tenexdos_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1338. {
  1339.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1340. }
  1341.  
  1342.  
  1343. char tenexdos_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1344. {
  1345.   return NIL;
  1346. }
  1347.  
  1348.  
  1349. char tenexdos_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  1350. {
  1351.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1352.   return (elt->recent && !elt->seen) ? T : NIL;
  1353. }
  1354.  
  1355. char tenexdos_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  1356. {
  1357.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1358. }
  1359.  
  1360.  
  1361. char tenexdos_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  1362. {
  1363.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1364. }
  1365.  
  1366.  
  1367. char tenexdos_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1368. {
  1369.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1370. }
  1371.  
  1372.  
  1373. char tenexdos_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1374. {
  1375.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1376. }
  1377.  
  1378.  
  1379. char tenexdos_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1380. {
  1381.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1382. }
  1383.  
  1384.  
  1385. char tenexdos_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1386. {
  1387.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1388. }
  1389.  
  1390.  
  1391. char tenexdos_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1392. {
  1393.   return NIL;
  1394. }
  1395.  
  1396.  
  1397. char tenexdos_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1398. {
  1399.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1400. }
  1401.  
  1402. char tenexdos_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1403. {
  1404.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1405.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1406. }
  1407.  
  1408.  
  1409. char tenexdos_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1410. {
  1411.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1412.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1413. }
  1414.  
  1415.  
  1416. char tenexdos_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1417. {
  1418.                 /* everybody interprets "since" as .GE. */
  1419.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1420.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1421. }
  1422.  
  1423. #define BUFLEN 4*MAILTMPLEN
  1424.  
  1425. char tenexdos_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1426. {
  1427.   char tmp[BUFLEN];
  1428.   unsigned long bufsize,hdrsize;
  1429.   unsigned long curpos = tenexdos_header (stream,msgno,&hdrsize);
  1430.   unsigned long textsize = mail_elt (stream,msgno)->rfc822_size - hdrsize;
  1431.                 /* get to header position */
  1432.   lseek (LOCAL->fd,curpos += hdrsize,SEEK_SET);
  1433.   while (textsize) {
  1434.     bufsize = min (textsize,(unsigned long) BUFLEN);
  1435.     read (LOCAL->fd,tmp,(unsigned int) bufsize);
  1436.     if (search (tmp,bufsize,d,n)) return T;
  1437.                 /* backtrack by pattern size if not at end */
  1438.     if (bufsize != textsize) bufsize -= n;
  1439.     textsize -= bufsize;    /* this many bytes handled */
  1440.     curpos += bufsize;        /* advance to that point */
  1441.     lseek (LOCAL->fd,curpos,SEEK_SET);
  1442.   }
  1443.   return NIL;            /* not found */
  1444. }
  1445.  
  1446.  
  1447. char tenexdos_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1448. {
  1449.   char *s = tenexdos_fetchstructure (stream,msgno,NIL)->subject;
  1450.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1451. }
  1452.  
  1453.  
  1454. char tenexdos_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1455. {
  1456.   char tmp[BUFLEN];
  1457.   unsigned long bufsize,hdrsize;
  1458.   unsigned long curpos = tenexdos_header (stream,msgno,&hdrsize);
  1459.   unsigned long textsize = mail_elt (stream,msgno)->rfc822_size;
  1460.                 /* get to header position */
  1461.   lseek (LOCAL->fd,curpos,SEEK_SET);
  1462.   while (textsize) {
  1463.     bufsize = min (textsize,(unsigned long) BUFLEN);
  1464.     read (LOCAL->fd,tmp,(unsigned int) bufsize);
  1465.     if (search (tmp,bufsize,d,n)) return T;
  1466.                 /* backtrack by pattern size if not at end */
  1467.     if (bufsize != textsize) bufsize -= n;
  1468.     textsize -= bufsize;    /* this many bytes handled */
  1469.     curpos += bufsize;        /* advance to that point */
  1470.     lseek (LOCAL->fd,curpos,SEEK_SET);
  1471.   }
  1472.   return NIL;            /* not found */
  1473. }
  1474.  
  1475. char tenexdos_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1476. {
  1477.   char tmp[8*MAILTMPLEN];
  1478.   tmp[0] = '\0';        /* initially empty string */
  1479.                 /* get text for address */
  1480.   rfc822_write_address (tmp,tenexdos_fetchstructure (stream,msgno,NIL)->bcc);
  1481.   return search (tmp,(long) strlen (tmp),d,n);
  1482. }
  1483.  
  1484.  
  1485. char tenexdos_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1486. {
  1487.   char tmp[8*MAILTMPLEN];
  1488.   tmp[0] = '\0';        /* initially empty string */
  1489.                 /* get text for address */
  1490.   rfc822_write_address (tmp,tenexdos_fetchstructure (stream,msgno,NIL)->cc);
  1491.   return search (tmp,(long) strlen (tmp),d,n);
  1492. }
  1493.  
  1494.  
  1495. char tenexdos_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1496. {
  1497.   char tmp[8*MAILTMPLEN];
  1498.   tmp[0] = '\0';        /* initially empty string */
  1499.                 /* get text for address */
  1500.   rfc822_write_address (tmp,tenexdos_fetchstructure (stream,msgno,NIL)->from);
  1501.   return search (tmp,(long) strlen (tmp),d,n);
  1502. }
  1503.  
  1504.  
  1505. char tenexdos_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1506. {
  1507.   char tmp[8*MAILTMPLEN];
  1508.   tmp[0] = '\0';        /* initially empty string */
  1509.                 /* get text for address */
  1510.   rfc822_write_address (tmp,tenexdos_fetchstructure (stream,msgno,NIL)->to);
  1511.   return search (tmp,(long) strlen (tmp),d,n);
  1512. }
  1513.  
  1514. /* Search parsers */
  1515.  
  1516.  
  1517. /* Parse a date
  1518.  * Accepts: function to return
  1519.  *        pointer to date integer to return
  1520.  * Returns: function to return
  1521.  */
  1522.  
  1523. search_t tenexdos_search_date (search_t f,long *n)
  1524. {
  1525.   long i;
  1526.   char *s;
  1527.   MESSAGECACHE elt;
  1528.                 /* parse the date and return fn if OK */
  1529.   return (tenexdos_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1530.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1531. }
  1532.  
  1533. /* Parse a flag
  1534.  * Accepts: function to return
  1535.  *        pointer to keyword integer to return
  1536.  *        MAIL stream
  1537.  * Returns: function to return
  1538.  */
  1539.  
  1540. search_t tenexdos_search_flag (search_t f,long *n,MAILSTREAM *stream)
  1541. {
  1542.   strtok (NIL," ");        /* slurp keyword */
  1543.   return f;
  1544. }
  1545.  
  1546. /* Parse a string
  1547.  * Accepts: function to return
  1548.  *        pointer to string to return
  1549.  *        pointer to string length to return
  1550.  * Returns: function to return
  1551.  */
  1552.  
  1553.  
  1554. search_t tenexdos_search_string (search_t f,char **d,long *n)
  1555. {
  1556.   char *end = " ";
  1557.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1558.   if (!c) return NIL;        /* missing argument */
  1559.   switch (*c) {            /* see what the argument is */
  1560.   case '{':            /* literal string */
  1561.     *n = strtol (c+1,d,10);    /* get its length */
  1562.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1563.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1564.       char e = *--c;
  1565.       *c = DELIM;        /* make sure not a space */
  1566.       strtok (c," ");        /* reset the strtok mechanism */
  1567.       *c = e;            /* put character back */
  1568.       break;
  1569.     }
  1570.   case '\0':            /* catch bogons */
  1571.   case ' ':
  1572.     return NIL;
  1573.   case '"':            /* quoted string */
  1574.     if (strchr (c+1,'"')) end = "\"";
  1575.     else return NIL;
  1576.   default:            /* atomic string */
  1577.     if (*d = strtok (c,end)) *n = strlen (*d);
  1578.     else return NIL;
  1579.     break;
  1580.   }
  1581.   return f;
  1582. }
  1583.